www.gusucode.com > VC++ 增强版CListCtrl控件(可日历选择、下拉列表、编辑等)-源码 > VC++ 增强版CListCtrl控件(可日历选择、下拉列表、编辑等)-源码程序/code/ListCtrlExt.cpp
// ListCtrlExt.cpp : implementation of the CListCtrlExt class // Downloload by http://www.NewXing.com #include "stdafx.h" #include "ListCtrlExt.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifndef HDF_SORTDOWN #define HDF_SORTDOWN 0x0200 #endif #ifndef HDF_SORTUP #define HDF_SORTUP 0x0400 #endif ///////////////////////////////////////////////////////////////////////////// // CListCtrlExt CListCtrlExt::EditorInfo::EditorInfo() :m_pfnInitEditor(NULL) ,m_pfnEndEditor(NULL) ,m_pWnd(NULL) ,m_bReadOnly(FALSE) { } CListCtrlExt::EditorInfo::EditorInfo(PFNEDITORCALLBACK pfnInitEditor, PFNEDITORCALLBACK pfnEndEditor, CWnd *pWnd) :m_pfnInitEditor(pfnInitEditor) ,m_pfnEndEditor(pfnEndEditor) ,m_pWnd(pWnd) ,m_bReadOnly(FALSE) { } CListCtrlExt::CellInfo::CellInfo(int nColumn) :m_clrBack(-1) ,m_clrText(-1) ,m_dwUserData(NULL) ,m_nColumn(nColumn) { } CListCtrlExt::CellInfo::CellInfo(int nColumn, COLORREF clrBack, COLORREF clrText, DWORD_PTR dwUserData) :m_clrBack(clrBack) ,m_clrText(clrText) ,m_dwUserData(dwUserData) ,m_nColumn(nColumn) { } CListCtrlExt::CellInfo::CellInfo(int nColumn, EditorInfo eiEditor, COLORREF clrBack, COLORREF clrText, DWORD_PTR dwUserData) :m_clrBack(clrBack) ,m_clrText(clrText) ,m_dwUserData(dwUserData) ,m_eiEditor(eiEditor) ,m_nColumn(nColumn) { } CListCtrlExt::CellInfo::CellInfo(int nColumn, EditorInfo eiEditor, DWORD_PTR dwUserData) :m_clrBack(-1) ,m_clrText(-1) ,m_dwUserData(dwUserData) ,m_eiEditor(eiEditor) ,m_nColumn(nColumn) { } CListCtrlExt::ColumnInfo::ColumnInfo(int nColumn) :m_eiEditor() ,m_clrBack(-1) ,m_clrText(-1) ,m_nColumn(nColumn) ,m_eSort(None) ,m_eCompare(NotSet) ,m_fnCompare(NULL) { } CListCtrlExt::ColumnInfo::ColumnInfo(int nColumn, PFNEDITORCALLBACK pfnInitEditor, PFNEDITORCALLBACK pfnEndEditor, CWnd *pWnd) :m_eiEditor(pfnInitEditor, pfnEndEditor, pWnd) ,m_nColumn(nColumn) ,m_clrBack(-1) ,m_clrText(-1) ,m_eSort(None) ,m_eCompare(NotSet) ,m_fnCompare(NULL) { } CListCtrlExt::ItemData::ItemData(DWORD_PTR dwUserData) :m_clrBack(0xFFFFFFFF) ,m_clrText(0xFFFFFFFF) ,m_dwUserData(dwUserData) { } CListCtrlExt::ItemData::ItemData(COLORREF clrBack, COLORREF clrText, DWORD_PTR dwUserData) :m_clrBack(clrBack) ,m_clrText(clrText) ,m_dwUserData(dwUserData) { } CListCtrlExt::ItemData::~ItemData() { while(m_aCellInfo.GetSize() > 0) { CellInfo *pInfo = (CellInfo*)m_aCellInfo.GetAt(0); m_aCellInfo.RemoveAt(0); delete pInfo; } } // CListCtrlExt IMPLEMENT_DYNAMIC(CListCtrlExt, CListCtrl) CListCtrlExt::CListCtrlExt() :m_pEditor(NULL) ,m_hAccel(NULL) ,m_nEditingRow(-1) ,m_MsgHook() ,m_nRow(-1) ,m_nColumn(-1) ,m_nSortColumn(-1) ,m_fnCompare(NULL) ,m_dwSortData(NULL) ,m_bColumnSort(FALSE) ,m_bGrid(FALSE) ,m_FocusCell(-1) ,m_nLastSearchCell(-1) ,m_nLastSearchRow(-1) ,m_nEditingColumn(-1) ,m_bHandleDelete(FALSE) ,m_pHeaderCtrl(NULL) { m_pHeaderCtrl = new CHeaderCtrlExt; } CListCtrlExt::~CListCtrlExt() { DeleteAllItemsData(); DeleteAllColumnInfo(); delete m_pHeaderCtrl; } BEGIN_MESSAGE_MAP(CListCtrlExt, CListCtrl) //{{AFX_MSG_MAP(CListCtrlExt) ON_WM_CHAR() ON_WM_KEYDOWN() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() ON_NOTIFY(HDN_ENDDRAG, 0, OnHdnEnddrag) ON_NOTIFY(NM_RCLICK, 0, OnNmRclickHeader) ON_NOTIFY(HDN_DIVIDERDBLCLICK, 0, OnHdnDividerdblclick) ON_NOTIFY_REFLECT_EX(NM_DBLCLK, CListCtrlExt::OnNMDblclk) ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, CListCtrlExt::OnNMCustomdraw) ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, CListCtrlExt::OnColumnclick) //}}AFX_MSG_MAP ON_MESSAGE(LVM_FINDITEM, OnFindItem) ON_MESSAGE(LVM_INSERTITEM, OnInsertItem) ON_MESSAGE(LVM_DELETEITEM, OnDeleteItem) ON_MESSAGE(LVM_INSERTCOLUMN, OnInsertColumn) ON_MESSAGE(LVM_DELETECOLUMN, OnDeleteColumn) ON_MESSAGE(LVM_DELETEALLITEMS, OnDeleteAllItems) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CListCtrlExt diagnostics #ifdef _DEBUG void CListCtrlExt::AssertValid() const { CListCtrl::AssertValid(); } void CListCtrlExt::Dump(CDumpContext& dc) const { CListCtrl::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CListCtrlExt message handlers void CListCtrlExt::PreSubclassWindow() { CListCtrl::PreSubclassWindow(); // TODO: Add your specialized code here and/or call the base class DWORD dwOldStyle = GetStyle(); ModifyStyle(LVS_TYPEMASK, LVS_REPORT); ::SetWindowLong(m_hWnd, GWL_STYLE, dwOldStyle); if(m_pHeaderCtrl->m_hWnd == NULL) { HWND hWnd = (HWND)::SendMessage(m_hWnd,LVM_GETHEADER,0,0); m_pHeaderCtrl->SubclassWindow(hWnd); m_clrDefBack = GetTextBkColor() | 0xFF000000; m_clrDefText = GetTextColor(); } } BOOL CListCtrlExt::PreTranslateMessage(MSG* pMsg) { if((m_hAccel && GetParent() && GetFocus() == this && ::TranslateAccelerator(GetParent()->m_hWnd, m_hAccel, pMsg)))return TRUE; if(pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN)) { HideEditor(); SetItemState(GetNextItem(-1, LVNI_FOCUSED), LVIS_SELECTED, LVIS_SELECTED); } return CListCtrl::PreTranslateMessage(pMsg); } LRESULT CListCtrlExt::OnInsertColumn(WPARAM wParam, LPARAM lParam) { int nPos = (int)Default(); // call default control procedure LPLVCOLUMN pCol = (LPLVCOLUMN)lParam; CHeaderCtrlExt::CItemData* pData = new CHeaderCtrlExt::CItemData(pCol->cx, TRUE, TRUE); m_pHeaderCtrl->SetItemData((int)wParam, (DWORD_PTR)pData); return nPos; } LRESULT CListCtrlExt::OnDeleteColumn(WPARAM wParam, LPARAM lParam) { CHeaderCtrlExt::CItemData* pData = (CHeaderCtrlExt::CItemData*)m_pHeaderCtrl->GetItemData((int)wParam); if(pData)delete pData; return Default(); } LRESULT CListCtrlExt::OnInsertItem(WPARAM wParam, LPARAM lParam) { int nPos = (int)Default(); LPLVITEM pItem = (LPLVITEM)lParam; SetItemData(nPos, pItem->iItem); return nPos; } LRESULT CListCtrlExt::OnDeleteItem(WPARAM wParam, LPARAM lParam) { DeleteItemData((int)wParam); return Default(); } LRESULT CListCtrlExt::OnDeleteAllItems(WPARAM wParam, LPARAM lParam) { DeleteAllItemsData(); return Default(); } LRESULT CListCtrlExt::OnFindItem(WPARAM wParam, LPARAM lParam) { int nPos = (int)Default(); LVFINDINFO* pLVFindItem = (LVFINDINFO*)lParam; if(pLVFindItem->flags & LVIF_PARAM) { int nStart = (int)wParam; int nCount = GetItemCount(); for(int i = nStart + 1;i < nCount;++i) { if(pLVFindItem->lParam == (int)GetItemData(i)) { nPos = i; break; } } } return nPos; } BOOL CListCtrlExt::EnsureSubItemVisible(int nItem, int nSubItem, CRect* pRect) { BOOL bReturn = EnsureVisible(nItem, FALSE); CRect rect,rcList; GetClientRect(&rcList); GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rect); if(rect.right > rcList.Width())Scroll(CSize(rect.Width() > rcList.Width() ? rect.left : rect.right - rcList.Width(),0)); if(rect.left < 0)Scroll(CSize(rect.left)); if(pRect) { GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rect); rect.right = min(rect.right, rcList.Width() - 4); *pRect = rect; } return bReturn; } int CListCtrlExt::GetColumnCount() { if(m_pHeaderCtrl)return m_pHeaderCtrl->GetItemCount(); int i = 0; LVCOLUMN col; col.mask = LVCF_WIDTH; while(GetColumn(i++, &col)); return i; } BOOL CListCtrlExt::AddItem(int nItemIndex, int nSubItemIndex, LPCTSTR lpszItemText, int nImageIndex) { LV_ITEM lvItem; lvItem.mask = LVIF_TEXT; lvItem.iItem = nItemIndex; lvItem.iSubItem = nSubItemIndex; lvItem.pszText = (LPTSTR)lpszItemText; if(nImageIndex != -1) { lvItem.mask |= LVIF_IMAGE; lvItem.iImage |= LVIF_IMAGE; } if(nSubItemIndex == 0)return InsertItem(&lvItem); return SetItem(&lvItem); } void CListCtrlExt::SelectItem(int nItem, BOOL bSelect) { int nIndex = -1; if(nItem >= 0)SetItemState(nItem, (bSelect ? LVIS_SELECTED: 0), LVIS_SELECTED); else { while((nIndex = GetNextItem(nIndex, bSelect ? LVNI_ALL : LVNI_SELECTED)) >= 0) { SetItemState(nIndex, (bSelect ? LVIS_SELECTED: 0), LVIS_SELECTED); } } } BOOL CListCtrlExt::Reset() { return (DeleteAllItems() && DeleteAllColumns()); } void CListCtrlExt::DeleteSelectedItems() { int nItem = -1; while((nItem = GetNextItem(-1, LVNI_SELECTED)) >= 0) DeleteItem(nItem); } void CListCtrlExt::HandleDeleteKey(BOOL bHandle) { m_bHandleDelete = bHandle; } DWORD_PTR CListCtrlExt::GetItemData(int nItem) const { ItemData* pData = (ItemData*)GetItemDataInternal(nItem); if(! pData)return NULL; return pData->m_dwUserData; } DWORD_PTR CListCtrlExt::GetItemDataInternal(int nItem) const { return CListCtrl::GetItemData(nItem); } BOOL CListCtrlExt::SetItemData(int nItem, DWORD_PTR dwData) { ItemData* pData = (ItemData*)GetItemDataInternal(nItem); if(pData)pData->m_dwUserData = dwData; else { pData = new ItemData(dwData); m_aItemData.Add(pData); } return CListCtrl::SetItemData(nItem, (DWORD_PTR)pData); } int CListCtrlExt::GetItemIndexFromData(DWORD_PTR dwData) { LVFINDINFO find; find.flags = LVFI_PARAM; find.lParam = dwData; return CListCtrl::FindItem(&find); } BOOL CListCtrlExt::SetCellData(int nItem, int nSubItem, DWORD_PTR dwData) { if(nItem < 0 || nItem >= GetItemCount() || nSubItem < 0 || nSubItem >= GetColumnCount())return FALSE; CellInfo* pCellInfo = GetCellInfo(nItem, nSubItem); if(! pCellInfo) { pCellInfo = new CellInfo(nSubItem); ItemData* pData = (ItemData*)GetItemDataInternal(nItem); if(! pData) { SetItemData(nItem, 0); pData = (ItemData*)GetItemDataInternal(nItem); } pData->m_aCellInfo.Add(pCellInfo); } pCellInfo->m_dwUserData = dwData; return TRUE; } DWORD_PTR CListCtrlExt::GetCellData(int nItem, int nSubItem) { CellInfo* pCellInfo = GetCellInfo(nItem, nSubItem); if(pCellInfo)return pCellInfo->m_dwUserData; else return 0; } BOOL CListCtrlExt::DeleteAllColumns() { while(DeleteColumn(0)); return(GetColumnCount() == 0); } BOOL CListCtrlExt::DeleteAllItemsData() { while(m_aItemData.GetSize() > 0) { ItemData* pData = (ItemData*)m_aItemData.GetAt(0); if(pData)delete pData; m_aItemData.RemoveAt(0); } return TRUE; } BOOL CListCtrlExt::DeleteItemData(int nItem) { if(nItem < 0 || nItem > GetItemCount())return FALSE; ItemData* pData = (ItemData*)CListCtrl::GetItemData(nItem); INT_PTR nCount = m_aItemData.GetSize(); for(INT_PTR i = 0; i < nCount && pData;++i) { if(m_aItemData.GetAt(i) == pData) { m_aItemData.RemoveAt(i); break; } } if(pData)delete pData; return TRUE; } CListCtrlExt::ColumnInfo* CListCtrlExt::GetColumnInfo(int nColumn) { INT_PTR nCount = m_aColumnInfo.GetSize(); for(INT_PTR i = 0;i < nCount;++i) { ColumnInfo* pColInfo = (ColumnInfo*)m_aColumnInfo.GetAt(i); if(pColInfo->m_nColumn == nColumn)return pColInfo; } return NULL; } CListCtrlExt::CellInfo* CListCtrlExt::GetCellInfo(int nItem, int nSubItem) { ItemData* pData = (ItemData*)GetItemDataInternal(nItem); if(pData == NULL)return NULL; INT_PTR nCount = pData->m_aCellInfo.GetSize(); for(INT_PTR i = 0;i < nCount;++i) { CellInfo* pInfo = (CellInfo*)pData->m_aCellInfo.GetAt(i); if(pInfo && pInfo->m_nColumn == nSubItem)return pInfo; } return NULL; } BOOL CListCtrlExt::DeleteAllColumnInfo() { while(m_aColumnInfo.GetSize() > 0) { ColumnInfo* pData = (ColumnInfo*)m_aColumnInfo.GetAt(0); if(pData)delete pData; m_aColumnInfo.RemoveAt(0); } return TRUE; } BOOL CListCtrlExt::DeleteColumnInfo(int nColumn) { if(nColumn < 0 || nColumn > GetColumnCount())return FALSE; INT_PTR nCount = m_aColumnInfo.GetSize(); ColumnInfo* pData = (ColumnInfo*)GetColumnInfo(nColumn); for(INT_PTR i = 0; i < nCount && pData;++i) { if(m_aColumnInfo.GetAt(i) == pData) { m_aColumnInfo.RemoveAt(i); break; } } if(pData)delete pData; return TRUE; } void CListCtrlExt::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLVCUSTOMDRAW lplvcd = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR); *pResult = 0; int iCol = lplvcd->iSubItem; int iRow = lplvcd->nmcd.dwItemSpec; int nRowItemData = (int)lplvcd->nmcd.lItemlParam; switch(lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT: *pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for subitem notifications. break; case CDDS_ITEMPREPAINT: *pResult = CDRF_NOTIFYSUBITEMDRAW; if(lplvcd->nmcd.uItemState & CDIS_FOCUS) { // If drawing focus row, then remove focus state and request to draw it later // - Row paint request can come twice, with and without focus flag // - Only respond to the one with focus flag, else DrawFocusRect XOR will cause solid or blank focus-rectangle if(GetNextItem(-1, LVNI_FOCUSED) == iRow) { if(m_FocusCell >= 0) { // We want to draw a cell-focus-rectangle instead of row-focus-rectangle lplvcd->nmcd.uItemState &= ~CDIS_FOCUS; *pResult |= CDRF_NOTIFYPOSTPAINT; } else { // Avoid bug where bottom of focus rectangle is missing when using grid-lines // - Draw the focus-rectangle for the entire row (explicit) lplvcd->nmcd.uItemState &= ~CDIS_FOCUS; *pResult |= CDRF_NOTIFYPOSTPAINT; } } } break; case CDDS_ITEMPREPAINT | CDDS_SUBITEM: { COLORREF clrBack = 0xFFFFFFFF; COLORREF clrText = 0xFFFFFFFF; *pResult = CDRF_DODEFAULT; CellInfo *pCell = GetCellInfo(iRow, iCol); if(pCell) { clrBack = pCell->m_clrBack; clrText = pCell->m_clrText; } if(clrBack == 0xFFFFFFFF && clrText == 0xFFFFFFFF) { ItemData* pData = (ItemData*)GetItemDataInternal(iRow); if(pData) { clrBack = pData->m_clrBack; clrText = pData->m_clrText; } } if(clrBack == 0xFFFFFFFF && clrText == 0xFFFFFFFF) { ColumnInfo* pInfo = GetColumnInfo(iCol); if(pInfo) { clrBack = pInfo->m_clrBack; clrText = pInfo->m_clrText; } } if(clrBack != 0xFFFFFFFF) { lplvcd->clrTextBk = clrBack; *pResult = CDRF_NEWFONT; } else { if(clrBack != m_clrDefBack) { lplvcd->clrTextBk = m_clrDefBack; *pResult = CDRF_NEWFONT; } } if(clrText != 0xFFFFFFFF) { lplvcd->clrText = clrText; *pResult = CDRF_NEWFONT; } else { if(clrText != m_clrDefText) { lplvcd->clrText = m_clrDefText; *pResult = CDRF_NEWFONT; } } // Remove the selection color for the focus cell, to make it easier to see focus if(lplvcd->nmcd.uItemState & CDIS_SELECTED && m_FocusCell == iCol && GetNextItem(-1, LVNI_FOCUSED) == iRow) { lplvcd->nmcd.uItemState &= ~CDIS_SELECTED; } } break; case CDDS_ITEMPOSTPAINT: if(m_bGrid && (GetExtendedStyle() & LVS_EX_FULLROWSELECT)) { if(GetNextItem(-1, LVNI_FOCUSED) != iRow)break; // Perform the drawing of the focus rectangle if(m_FocusCell >= 0) { // Draw the focus-rectangle for a single-cell CRect rcHighlight; CDC* pDC = CDC::FromHandle(lplvcd->nmcd.hdc); VERIFY(GetCellRect(iRow, m_FocusCell, rcHighlight)); // Adjust rectangle according to grid-lines int cxborder = ::GetSystemMetrics(SM_CXBORDER); // Columns after the first visible column, has to take account of left-grid-border if(m_pHeaderCtrl->OrderToIndex(m_FocusCell) != 0)rcHighlight.left += cxborder; rcHighlight.bottom -= cxborder; pDC->DrawFocusRect(rcHighlight); } else { // Avoid bug where bottom of focus rectangle is missing when using grid-lines // - Draw the focus-rectangle for the entire row (explicit) CRect rcHighlight; CDC* pDC = CDC::FromHandle(lplvcd->nmcd.hdc); // Using LVIR_BOUNDS to get the entire row-rectangle VERIFY(GetItemRect(iRow, rcHighlight, LVIR_BOUNDS)); int cxborder = ::GetSystemMetrics(SM_CXBORDER); rcHighlight.bottom -= cxborder; pDC->DrawFocusRect(rcHighlight); } } break; default: *pResult = CDRF_DODEFAULT; break; } } void CListCtrlExt::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* phdr = reinterpret_cast<NM_LISTVIEW*>(pNMHDR); SortOnColumn(phdr->iSubItem, TRUE); *pResult = 0; } BOOL CListCtrlExt::OnNMDblclk(NMHDR* pNMHDR, LRESULT* pResult) { if(m_bGrid && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT && (GetExtendedStyle() & LVS_EX_FULLROWSELECT)) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; if(pNMListView) { int nItem = pNMListView->iItem, nSubItem = pNMListView->iSubItem; *pResult = DisplayEditor(nItem, nSubItem); } } return *pResult; } void CListCtrlExt::OnLButtonDown(UINT nFlags, CPoint point) { if(! m_bGrid || (GetStyle() & LVS_TYPEMASK) != LVS_REPORT || ! (GetExtendedStyle() & LVS_EX_FULLROWSELECT)) { CListCtrl::OnLButtonDown(nFlags, point); return; } // Find out what subitem was clicked LVHITTESTINFO hitinfo = {0}; hitinfo.pt = point; hitinfo.flags = nFlags; SubItemHitTest(&hitinfo); // Update the focused cell before calling CListCtrl::OnLButtonDown() // as it might cause a row-repaint m_FocusCell = hitinfo.iSubItem; CListCtrl::OnLButtonDown(nFlags, point); // CListCtrl::OnLButtonDown() doesn't always cause a row-repaint // call our own method to ensure the row is repainted UpdateFocusCell(hitinfo.iSubItem); } void CListCtrlExt::OnRButtonDown(UINT nFlags, CPoint point) { if(! m_bGrid || (GetStyle() & LVS_TYPEMASK) != LVS_REPORT || ! (GetExtendedStyle() & LVS_EX_FULLROWSELECT)) { CListCtrl::OnRButtonDown(nFlags, point); return; } // Find out what subitem was clicked LVHITTESTINFO hitinfo = {0}; hitinfo.flags = nFlags; hitinfo.pt = point; SubItemHitTest(&hitinfo); // If not right-clicking on an actual row, then don't update focus cell if(hitinfo.iItem == -1) { CListCtrl::OnRButtonDown(nFlags, point); return; } // Update the focused cell before calling CListCtrl::OnLButtonDown() // as it might cause a row-repaint m_FocusCell = hitinfo.iSubItem; CListCtrl::OnRButtonDown(nFlags, point); // CListCtrl::OnLButtonDown() doesn't always cause a row-repaint // call our own method to ensure the row is repainted UpdateFocusCell(hitinfo.iSubItem); } void CListCtrlExt::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // Catch event before the parent listctrl gets it to avoid extra scrolling // - OBS! This can also prevent the key-events to reach LVN_KEYDOWN handlers if(! m_bGrid || (GetStyle() & LVS_TYPEMASK) != LVS_REPORT || ! (GetExtendedStyle() & LVS_EX_FULLROWSELECT)) { CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags); return; } switch(nChar) { case VK_RIGHT: MoveFocusCell(TRUE); return; // Do not allow scroll case VK_LEFT: MoveFocusCell(FALSE); return; // Do not allow scroll case 0x41: // CTRL+A (Select all rows) if(GetKeyState(VK_CONTROL) < 0) { if(! (GetStyle() & LVS_SINGLESEL)) { SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED); } } break; case VK_F2: DisplayEditor(GetNextItem(-1, LVNI_SELECTED),m_FocusCell); break; } CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags); } // Keyboard search with subitems void CListCtrlExt::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { if(m_FocusCell <= 0) { CListCtrl::OnChar(nChar, nRepCnt, nFlags); return; } if(GetKeyState(VK_CONTROL) < 0) { m_sLastSearchString = _T(""); return; } // No input within 2 seconds, resets the search if(m_tLastSearchTime.GetCurrentTime() >= (m_tLastSearchTime + 2) && m_sLastSearchString.GetLength() > 0)m_sLastSearchString = _T(""); // Changing cells, resets the search if(m_nLastSearchCell != m_FocusCell)m_sLastSearchString = _T(""); // Changing rows, resets the search if(m_nLastSearchRow != GetNextItem(-1, LVNI_FOCUSED))m_sLastSearchString = _T(""); m_nLastSearchCell = m_FocusCell; m_tLastSearchTime = m_tLastSearchTime.GetCurrentTime(); if(m_sLastSearchString.GetLength() == 1 && (UINT)m_sLastSearchString.GetAt(0) == nChar) { // When the same first character is entered again, // then just repeat the search } else m_sLastSearchString += (TCHAR)nChar; int nRow = GetNextItem(-1, LVNI_FOCUSED); if(nRow < 0)nRow = 0; int nCol = m_FocusCell; if(nCol < 0)nCol = m_pHeaderCtrl->OrderToIndex(0); int nRowCount = GetItemCount(); // Perform the search loop twice // - First search from current position down to bottom // - Then search from top to current position for(int j = 0;j < 2;++j) { for(int i = nRow + 1;i < nRowCount;++i) { CString cellText = GetItemText(i, nCol); if(cellText.GetLength() >= m_sLastSearchString.GetLength()) { cellText = cellText.Left(m_sLastSearchString.GetLength()); if(cellText.CompareNoCase(m_sLastSearchString) == 0) { // De-select all other rows SetItemState(-1, 0, LVIS_SELECTED); // Select row found SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); // Focus row found SetItemState(i, LVIS_FOCUSED, LVIS_FOCUSED); // Scroll to row found EnsureVisible(i, FALSE); m_nLastSearchRow = i; return; } } } nRowCount = nRow; nRow = -1; } } void CListCtrlExt::OnNmRclickHeader(NMHDR* pNMHDR, LRESULT* pResult) { LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR); // TODO: Add your control notification handler code here CPoint point; GetCursorPos(&point); int nHeaderItemCount = m_pHeaderCtrl->GetItemCount(); int nHeaderRemovableItemCount = m_pHeaderCtrl->GetRemovableItemCount(); CMenu menu; if(nHeaderItemCount > nHeaderRemovableItemCount && menu.CreatePopupMenu()) { const int TEXT_LEN = 64; const TCHAR TEXT_TAIL[] = _T("..."); TCHAR szText[TEXT_LEN + sizeof(TEXT_TAIL) - 1]; HDITEM hdi; hdi.mask = HDI_TEXT; hdi.pszText = szText; hdi.cchTextMax = TEXT_LEN; int nCount = m_pHeaderCtrl->GetItemCount(); for(int i = 0;i < nCount;++i) { if(! m_pHeaderCtrl->GetItem(i, &hdi))return; if(hdi.cchTextMax == TEXT_LEN - 1)_tcscat(szText, TEXT_TAIL); UINT nFlags = MF_STRING; if(! m_pHeaderCtrl->GetRemovable(i))nFlags |= MF_GRAYED | MF_CHECKED; if(m_pHeaderCtrl->GetVisible(i))nFlags |= MF_CHECKED; if(! menu.AppendMenu(nFlags, i + 1, szText))return; } UINT nIndex = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, this); if(nIndex > 0) { nIndex--; BOOL bVisible = m_pHeaderCtrl->GetVisible(nIndex); m_pHeaderCtrl->SetVisible(nIndex,! bVisible); } } *pResult = 0; } void CListCtrlExt::OnHdnDividerdblclick(NMHDR* pNMHDR, LRESULT* pResult) { LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR); // TODO: Add your control notification handler code here *pResult = 1; int nItem = m_pHeaderCtrl->FindVisibleItem(phdr->iItem); if(nItem >= 0) { int nCount = GetItemCount(); if(nCount > 0) { CString sTemp; int nMaxWidth = 0; CClientDC dc(this); CFont* pOldFont = dc.SelectObject(GetFont()); for(int i = 0;i < nCount;++i) { sTemp = GetItemText(i, nItem); int w = GetStringWidth(sTemp); nMaxWidth = max(nMaxWidth, w + 12); } CImageList* pImgList = GetImageList(LVSIL_SMALL); if(pImgList != NULL && nItem == 0) { int cx, cy; ImageList_GetIconSize(pImgList->m_hImageList, &cx, &cy); nMaxWidth += cx; } SetColumnWidth(nItem, nMaxWidth); dc.SelectObject(pOldFont); } } } void CListCtrlExt::OnHdnEnddrag(NMHDR* pNMHDR, LRESULT* pResult) { LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR); // TODO: Add your control notification handler code here m_pHeaderCtrl->PostMessage(WM_HDN_ENDDRAG, 0, 0L); *pResult = 0; } int CALLBACK CListCtrlExt::CompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { int nSort = 0; int nCompare = 0; ColumnInfo* pColInfo; CListCtrlExt* This = reinterpret_cast<CListCtrlExt*>(lParamSort); if(! This)return 0; if(! (This->m_nSortColumn < 0 || This->m_nSortColumn >= This->GetColumnCount())) { pColInfo = This->GetColumnInfo(This->m_nSortColumn); if(pColInfo && (pColInfo->m_eSort & SortBits)) { nSort = pColInfo->m_eSort & Ascending ? 1 : -1 ; if(! This->m_fnCompare && pColInfo->m_fnCompare)This->m_fnCompare = pColInfo->m_fnCompare; } } if(This->m_fnCompare && This->m_fnCompare != &CListCtrlExt::CompareProc) { ItemData* pD1 = reinterpret_cast<ItemData*>(lParam1); ItemData* pD2 = reinterpret_cast<ItemData*>(lParam2); if(pD1)lParam1 = pD1->m_dwUserData; if(pD2)lParam2 = pD2->m_dwUserData; nCompare = This->m_fnCompare(lParam1, lParam2, This->m_dwSortData ? This->m_dwSortData : This->m_nSortColumn); if(! This->m_dwSortData && nSort)return nCompare * nSort; } if(! nSort)return 0; int nLeft = This->GetItemIndexFromData(lParam1); int nRight = This->GetItemIndexFromData(lParam2); if(nLeft < 0) nLeft = lParam1; if(nRight < 0) nRight = lParam2; int nCount = This->GetItemCount(); if(nLeft < 0 || nRight < 0 || nLeft >= nCount || nRight >= nCount)return 0; nCompare = Compare(nLeft, nRight, lParamSort); return nCompare * nSort; } BOOL CListCtrlExt::SortItems(PFNLVCOMPARE pfnCompare, DWORD_PTR dwData) { int nCount = GetItemCount(); DWORD_PTR dwEditingItemData = 0; if(m_nEditingRow >= 0 && m_nEditingRow < nCount)dwEditingItemData = GetItemDataInternal(m_nEditingRow); CString dbg; dbg.Format("\nBefore : %d", m_nEditingRow); OutputDebugString(dbg); m_fnCompare = pfnCompare; m_dwSortData = dwData; BOOL bReturn = CListCtrl::SortItems(CompareProc, (DWORD_PTR)this); m_fnCompare = NULL; m_dwSortData = NULL; if(dwEditingItemData)m_nEditingRow = GetItemIndexFromData(dwEditingItemData); dbg.Format("\nAfter : %d", m_nEditingRow); OutputDebugString(dbg); return bReturn; } BOOL CListCtrlExt::SortOnColumn(int nColumn, BOOL bChangeOrder) { ColumnInfo* pColInfo; if((pColInfo = GetColumnInfo(nColumn)) && (pColInfo->m_eSort & SortBits)) { if(pColInfo->m_eSort & Auto) { pColInfo->m_eSort =(Sort)((pColInfo->m_eSort & (Ascending | Descending)) ? pColInfo->m_eSort : pColInfo->m_eSort | Descending); if(bChangeOrder)pColInfo->m_eSort = (Sort)(pColInfo->m_eSort ^ (Ascending | Descending)); } HDITEM hd; hd.mask = HDI_FORMAT; m_pHeaderCtrl->GetItem(m_nSortColumn, &hd); hd.fmt = hd.fmt & ~(HDF_SORTDOWN | HDF_SORTUP); m_pHeaderCtrl->SetItem(m_nSortColumn, &hd); m_nSortColumn = nColumn; CListCtrl::SortItems(CompareProc, (DWORD_PTR)this); m_pHeaderCtrl->GetItem(nColumn, &hd); hd.fmt = hd.fmt & ~(HDF_SORTDOWN | HDF_SORTUP); hd.fmt |= pColInfo->m_eSort & Ascending ? HDF_SORTUP : HDF_SORTDOWN; m_pHeaderCtrl->SetItem(nColumn, &hd); // to store of which column and direction was clicked m_bColumnSort = pColInfo->m_eSort & Ascending ? TRUE : FALSE; return TRUE; } return FALSE; } void CListCtrlExt::SetColumnSorting(int nColumn, Sort eSort, PFNLVCOMPARE fnCallBack) { if(nColumn < 0 || nColumn >= GetColumnCount() || ! (eSort & SortBits))return; ColumnInfo* pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(! pInfo)SetColumnEditor(nColumn, (CWnd*)NULL); pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(pInfo) { pInfo->m_eSort = eSort; pInfo->m_eCompare = NotSet; pInfo->m_fnCompare = fnCallBack; } } int CListCtrlExt::Compare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { CListCtrlExt* This = (CListCtrlExt*)lParamSort; if(! This || This->m_nSortColumn < 0 || This->m_nSortColumn >= This->GetColumnCount())return 0; int nSubItem = This->m_nSortColumn; ColumnInfo* pInfo = This->GetColumnInfo(nSubItem); if(! pInfo)return 0; CString sLeft = This->GetItemText(lParam1, nSubItem); CString sRight = This->GetItemText(lParam2, nSubItem); switch(pInfo->m_eCompare) { case Int: return CompareInt(sLeft, sRight); case Double: return CompareDouble(sLeft, sRight); case StringNoCase: return CompareStringNoCase(sLeft, sRight); case StringNumber: return CompareNumberString(sLeft, sRight); case StringNumberNoCase: return CompareNumberStringNoCase(sLeft, sRight); case String: return CompareString(sLeft, sRight); case Date: return CompareDate(sLeft, sRight); case NotSet: return 0; default: return CompareString(sLeft, sRight); } return CompareString(sLeft, sRight); } int CListCtrlExt::CompareInt(LPCSTR pLeftText, LPCSTR pRightText) { return (int)(atol(pLeftText) - atol(pRightText)); } int CListCtrlExt::CompareDouble(LPCSTR pLeftText, LPCSTR pRightText) { return (int)(atof(pLeftText) - atof(pRightText)); } int CListCtrlExt::CompareString(LPCSTR pLeftText, LPCSTR pRightText) { return CString(pLeftText).Compare(pRightText); } int CListCtrlExt::CompareNumberString(LPCSTR pLeftText, LPCSTR pRightText) { LONGLONG l1 = atol(pLeftText); LONGLONG l2 = atol(pRightText); if(l1 && l2 && (l1 - l2)) { CString sTemp1, sTemp2; sTemp1.Format("%ld", l1); sTemp2.Format("%ld", l2); CString left(pLeftText); CString right(pRightText); if(sTemp1.GetLength() == left.GetLength() && sTemp2.GetLength() == right.GetLength())return (int)(l1 - l2); } return CString(pLeftText).Compare(pRightText); } int CListCtrlExt::CompareNumberStringNoCase(LPCSTR pLeftText, LPCSTR pRightText) { LONGLONG l1 = atol(pLeftText); LONGLONG l2 = atol(pRightText); if(l1 && l2 && (l1 - l2)) { CString sTemp1, sTemp2; sTemp1.Format("%ld",l1); sTemp2.Format("%ld",l2); CString left(pLeftText); CString right(pRightText); if(sTemp1.GetLength() == left.GetLength() && sTemp2.GetLength() == right.GetLength())return (int)(l1 - l2); } return CString(pLeftText).CompareNoCase(pRightText); } int CListCtrlExt::CompareStringNoCase(LPCSTR pLeftText, LPCSTR pRightText) { return CString(pLeftText).CompareNoCase(pRightText); } int CListCtrlExt::CompareDate(LPCSTR pLeftText, LPCSTR pRightText) { COleDateTime dL, dR; dL.ParseDateTime(pLeftText); dR.ParseDateTime(pRightText); return (dL == dR ? 0 : (dL < dR ? -1 : 1)); } void CListCtrlExt::SetColumnSorting(int nColumn, Sort eSort, Comparer eComparer) { if(nColumn < 0 || nColumn >= GetColumnCount() || ! (eSort & SortBits))return; ColumnInfo* pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(! pInfo)SetColumnEditor(nColumn, (CWnd*)NULL); pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(pInfo) { pInfo->m_eSort = eSort; pInfo->m_eCompare = eComparer; pInfo->m_fnCompare = NULL; } } void CListCtrlExt::SetColumnReadOnly(int nColumn, BOOL bReadOnly) { if(nColumn < 0 || nColumn >= GetColumnCount())return; ColumnInfo* pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(! pInfo)SetColumnEditor(nColumn, (CWnd*)NULL); pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(pInfo)pInfo->m_eiEditor.m_bReadOnly = bReadOnly; } void CListCtrlExt::SetCellReadOnly(int nRow, int nColumn, BOOL bReadOnly ) { if(nRow < 0 || nRow >= GetItemCount() || nColumn < 0 || nColumn >= GetColumnCount())return; CellInfo* pInfo = (CellInfo*)GetCellInfo(nRow, nColumn); if(! pInfo)SetCellEditor(nRow, nColumn, (CWnd*)NULL); pInfo = (CellInfo*)GetCellInfo(nRow, nColumn); if(pInfo)pInfo->m_eiEditor.m_bReadOnly = bReadOnly; } void CListCtrlExt::SetRowReadOnly(int nRow, BOOL bReadOnly) { if(nRow < 0 || nRow >= GetItemCount())return; ItemData* pInfo = (ItemData*)GetItemDataInternal(nRow); if(! pInfo)SetItemData(nRow, 0); pInfo = (ItemData*)GetItemDataInternal(nRow); if(pInfo)pInfo->m_eiEditor.m_bReadOnly = bReadOnly; } BOOL CListCtrlExt::IsColumnReadOnly(int nColumn) { if(nColumn < 0 || nColumn >= GetColumnCount())return FALSE; ColumnInfo *pInfo = (ColumnInfo*)GetColumnInfo(nColumn); if(pInfo)return pInfo->m_eiEditor.m_bReadOnly; return FALSE; } BOOL CListCtrlExt::IsColumnHidden(int nColumn) { return ! m_pHeaderCtrl->GetVisible(nColumn); } BOOL CListCtrlExt::IsRowReadOnly(int nRow) { if(nRow < 0 || nRow >= GetItemCount())return FALSE; ItemData* pInfo = (ItemData*)GetItemDataInternal(nRow); if(pInfo)return pInfo->m_eiEditor.m_bReadOnly; return FALSE; } BOOL CListCtrlExt::IsCellReadOnly(int nRow, int nColumn) { if(nRow < 0 || nRow >= GetItemCount() || nColumn < 0 || nColumn >= GetColumnCount())return FALSE; CellInfo* pInfo = (CellInfo*)GetCellInfo(nRow, nColumn); if(pInfo)return pInfo->m_eiEditor.m_bReadOnly; else return (IsRowReadOnly(nRow) || IsColumnReadOnly(nColumn)); } void CListCtrlExt::SetRowColors(int nItem, COLORREF clrBk, COLORREF clrText) { ItemData* pData = (ItemData*)GetItemDataInternal(nItem); if(! pData)SetItemData(nItem, 0); pData = (ItemData*)GetItemDataInternal(nItem); if(! pData)return; pData->m_clrText = clrText; pData->m_clrBack = clrBk; Update(nItem); } void CListCtrlExt::SetColumnColors(int nColumn, COLORREF clrBack, COLORREF clrText) { if(nColumn < 0 || nColumn >= GetColumnCount())return; ColumnInfo* pColInfo = GetColumnInfo(nColumn); if(! pColInfo) { pColInfo = new ColumnInfo(nColumn); m_aColumnInfo.Add(pColInfo); } if(pColInfo) { pColInfo->m_clrBack = clrBack; pColInfo->m_clrText = clrText; } } void CListCtrlExt::SetCellColors(int nRow, int nColumn, COLORREF clrBack, COLORREF clrText) { if(nRow < 0 || nRow >= GetItemCount() || nColumn < 0 || nColumn >= GetColumnCount())return; CellInfo* pCellInfo = GetCellInfo(nRow, nColumn); if(! pCellInfo)SetCellData(nRow, nColumn, 0); pCellInfo = GetCellInfo(nRow, nColumn); if(pCellInfo) { pCellInfo->m_clrBack = clrBack; pCellInfo->m_clrText = clrText; } } void CListCtrlExt::SetColumnEditor(int nColumn, PFNEDITORCALLBACK pfnInitEditor, PFNEDITORCALLBACK pfnEndEditor, CWnd* pWnd) { ColumnInfo* pColInfo = GetColumnInfo(nColumn); if(! pColInfo) { pColInfo = new ColumnInfo(nColumn); m_aColumnInfo.Add(pColInfo); } if(pColInfo) { pColInfo->m_eiEditor.m_pfnInitEditor = pfnInitEditor; pColInfo->m_eiEditor.m_pfnEndEditor = pfnEndEditor; pColInfo->m_eiEditor.m_pWnd = pWnd; } } void CListCtrlExt::SetColumnEditor(int nColumn, CWnd* pWnd) { SetColumnEditor(nColumn, NULL, NULL, pWnd); } void CListCtrlExt::SetRowEditor(int nRow, CWnd* pWnd) { SetRowEditor(nRow, NULL, NULL, pWnd); } void CListCtrlExt::SetRowEditor(int nRow, PFNEDITORCALLBACK pfnInitEditor, PFNEDITORCALLBACK pfnEndEditor, CWnd* pWnd) { ItemData* pData = (ItemData*)GetItemDataInternal(nRow); if(! pData) { SetItemData(nRow, 0); pData = (ItemData*)GetItemDataInternal(nRow); } if(pData) { pData->m_eiEditor.m_pfnInitEditor = pfnInitEditor; pData->m_eiEditor.m_pfnEndEditor = pfnEndEditor; pData->m_eiEditor.m_pWnd = pWnd; } } BOOL CListCtrlExt::DisplayEditor(int nItem, int nSubItem) { int nCount = GetItemCount(); DWORD_PTR dwEditingItemData = 0; if(nItem >= 0 && nItem < nCount)dwEditingItemData = GetItemDataInternal(nItem); HideEditor(); if(dwEditingItemData)nItem = GetItemIndexFromData(dwEditingItemData); if(nItem < 0 || nItem > nCount || nSubItem < 0 || nSubItem > GetColumnCount() || IsColumnReadOnly(nSubItem) || IsRowReadOnly(nItem) || IsCellReadOnly(nItem, nSubItem) || IsColumnHidden(nSubItem))return FALSE; CRect rectSubItem; // GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rectSubItem); EnsureSubItemVisible(nItem, nSubItem, &rectSubItem); CellInfo* pCellInfo = GetCellInfo(nItem, nSubItem); ColumnInfo* pColInfo = GetColumnInfo(nSubItem); ItemData* pRowInfo = (ItemData*)GetItemDataInternal(nItem); BOOL bReadOnly = FALSE; m_pEditor = &m_eiDefEditor; if(pColInfo && ! (bReadOnly |= pColInfo->m_eiEditor.m_bReadOnly) && pColInfo->m_eiEditor.IsSet())m_pEditor = &pColInfo->m_eiEditor; if(pRowInfo && ! (bReadOnly |= pRowInfo->m_eiEditor.m_bReadOnly) && pRowInfo->m_eiEditor.IsSet())m_pEditor = &pRowInfo->m_eiEditor; if(pCellInfo && ! (bReadOnly |= pCellInfo->m_eiEditor.m_bReadOnly) && pCellInfo->m_eiEditor.IsSet())m_pEditor = &pCellInfo->m_eiEditor; if(bReadOnly || ! m_pEditor || ! m_pEditor->IsSet() || m_pEditor->m_bReadOnly) { m_pEditor = NULL; return FALSE; } m_nEditingRow = nItem; m_nEditingColumn = nSubItem; m_nRow = nItem; m_nColumn = nSubItem; CString sText = GetItemText(nItem, nSubItem); if(m_pEditor->m_pfnInitEditor)m_pEditor->m_pfnInitEditor(&m_pEditor->m_pWnd, nItem, nSubItem, sText, GetItemData(nItem), this, TRUE); if(! m_pEditor->m_pWnd)return FALSE; SelectItem(-1, FALSE); if(! m_pEditor->m_pfnInitEditor)m_pEditor->m_pWnd->SetWindowText(sText); m_pEditor->m_pWnd->SetParent(this); m_pEditor->m_pWnd->SetOwner(this); m_pEditor->m_pWnd->SetWindowPos(NULL, rectSubItem.left, rectSubItem.top, rectSubItem.Width(), rectSubItem.Height(), SWP_SHOWWINDOW); m_pEditor->m_pWnd->ShowWindow(SW_SHOW); m_pEditor->m_pWnd->SetFocus(); m_MsgHook.Attach(m_pEditor->m_pWnd->m_hWnd, this->m_hWnd); return TRUE; } void CListCtrlExt::HideEditor(BOOL bUpdate) { CSingleLock lock(&m_oLock, TRUE); if(lock.IsLocked() && m_MsgHook.Detach()) { if(m_pEditor && m_pEditor->m_pWnd) { m_pEditor->m_pWnd->ShowWindow(SW_HIDE); CString sText; DWORD_PTR dwData = 0; if(GetItemCount() > m_nEditingRow) { sText = GetItemText(m_nEditingRow, m_nEditingColumn); dwData = GetItemData(m_nEditingRow); } else bUpdate = FALSE; if(m_pEditor->m_pfnEndEditor)bUpdate = m_pEditor->m_pfnEndEditor(&m_pEditor->m_pWnd, m_nEditingRow, m_nEditingColumn, sText, dwData, this, bUpdate); else m_pEditor->m_pWnd->GetWindowText(sText); if(bUpdate)SetItemText(m_nEditingRow, m_nEditingColumn, sText); if(GetItemCount() > m_nEditingRow)Update(m_nEditingRow); if(bUpdate == -1)SortOnColumn(m_nEditingColumn); m_pEditor = NULL; } } lock.Unlock(); } void CListCtrlExt::SetCellEditor(int nRow, int nColumn, PFNEDITORCALLBACK pfnInitEditor, PFNEDITORCALLBACK pfnEndEditor, CWnd* pWnd) { if(nRow < 0 || nRow >= GetItemCount() || nColumn < 0 || nColumn >= GetColumnCount())return; CellInfo* pCellInfo = GetCellInfo(nRow, nColumn); if(! pCellInfo) { SetCellData(nRow, nColumn, 0); pCellInfo = (CellInfo*)GetCellInfo(nRow, nColumn); } if(pCellInfo) { pCellInfo->m_eiEditor.m_pfnInitEditor = pfnInitEditor; pCellInfo->m_eiEditor.m_pfnEndEditor = pfnEndEditor; pCellInfo->m_eiEditor.m_pWnd = pWnd; } } void CListCtrlExt::SetCellEditor(int nRow, int nColumn, CWnd* pWnd) { SetCellEditor(nRow, nColumn, NULL, NULL, pWnd); } void CListCtrlExt::SetDefaultEditor(PFNEDITORCALLBACK pfnInitEditor, PFNEDITORCALLBACK pfnEndEditor, CWnd* pWnd) { m_eiDefEditor.m_pfnInitEditor = pfnInitEditor; m_eiDefEditor.m_pfnEndEditor = pfnEndEditor; m_eiDefEditor.m_pWnd = pWnd; } void CListCtrlExt::SetDefaultEditor(CWnd* pWnd) { SetDefaultEditor(NULL, NULL, pWnd); } BOOL CListCtrlExt::SaveState() { CString sListName; CWinApp* pApp = (CWinApp*)AfxGetApp(); sListName.Format(_T("%d"),GetDlgCtrlID()); int nCount = m_pHeaderCtrl->GetItemCount(); pApp->WriteProfileInt(sListName,_T("ColumnCount"),nCount); int* nColOrder = new int[nCount]; m_pHeaderCtrl->GetOrderArray(nColOrder,nCount); pApp->WriteProfileBinary(sListName,_T("ColumnOrder"),(BYTE*)nColOrder,sizeof(int) * nCount); delete []nColOrder; int* nColWidth = new int[nCount]; m_pHeaderCtrl->GetWidthArray(nColWidth,nCount); pApp->WriteProfileBinary(sListName,_T("ColumnWidth"),(BYTE*)nColWidth,sizeof(int) * nCount); delete []nColWidth; int* nColVisible = new int[nCount]; m_pHeaderCtrl->GetVisibleArray(nColVisible,nCount); pApp->WriteProfileBinary(sListName,_T("ColumnVisible"),(BYTE*)nColVisible,sizeof(int) * nCount); delete []nColVisible; pApp->WriteProfileInt(sListName,_T("ColSortNum"),m_nSortColumn); pApp->WriteProfileInt(sListName,_T("ColSortDir"),m_bColumnSort); return TRUE; } BOOL CListCtrlExt::RestoreState() { CString sListName; CWinApp* pApp = (CWinApp*)AfxGetApp(); sListName.Format(_T("%d"),GetDlgCtrlID()); int nCount = pApp->GetProfileInt(sListName,_T("ColumnCount"), 0); if(nCount != m_pHeaderCtrl->GetItemCount())return FALSE; UINT nBytes; BOOL bReturn = FALSE; int* nColOrder = NULL; if((pApp->GetProfileBinary(sListName,_T("ColumnOrder"),(BYTE**)&nColOrder,&nBytes) && nBytes == sizeof(int) * nCount)) { bReturn = VerifyOrderArray(nColOrder,nCount) && m_pHeaderCtrl->SetOrderArray(nCount,nColOrder); ASSERT(bReturn); } delete []nColOrder; if(! bReturn)return FALSE; bReturn = FALSE; int* nColWidth = NULL; if((pApp->GetProfileBinary(sListName,_T("ColumnWidth"),(BYTE**)&nColWidth,&nBytes) && nBytes == sizeof(int) * nCount)) bReturn = m_pHeaderCtrl->SetWidthArray(nCount,nColWidth); delete []nColWidth; if(! bReturn)return FALSE; bReturn = FALSE; int* nColVisible = NULL; if((pApp->GetProfileBinary(sListName,_T("ColumnVisible"),(BYTE**)&nColVisible,&nBytes) && nBytes == sizeof(int) * nCount)) bReturn = m_pHeaderCtrl->SetVisibleArray(nCount,nColVisible); delete []nColVisible; int nColumnSort = pApp->GetProfileInt(sListName,_T("ColSortNum"),0); BOOL bColumnSort = pApp->GetProfileInt(sListName,_T("ColSortDir"),0); SortOnColumn(nColumnSort,bColumnSort); return bReturn; } BOOL CListCtrlExt::VerifyOrderArray(int* piArray, int nCount) { for(int i = 0;i < nCount;++i) { if(! (piArray[i] >= 0 && piArray[i] <= nCount - 1))return FALSE; // compare with items after current one for(int j = i + 1;j < nCount;++j) { if(piArray[i] == piArray[j])return FALSE; } } return TRUE; } void CListCtrlExt::UpdateFocusCell(int nCol) { m_FocusCell = nCol; // Update focus cell before starting re-draw int nFocusRow = GetNextItem(-1, LVNI_FOCUSED); if(nFocusRow >= 0) { CRect itemRect; VERIFY(GetItemRect(nFocusRow, itemRect, LVIR_BOUNDS)); InvalidateRect(itemRect); UpdateWindow(); } } void CListCtrlExt::MoveFocusCell(BOOL bRight) { if(GetItemCount() <= 0) { m_FocusCell = -1; // Entire row selected return; } if(m_FocusCell == -1) { // Entire row already selected if(bRight) { // Change to the first column in the current order m_FocusCell = m_pHeaderCtrl->OrderToIndex(0); } } else { // Convert focus-cell to order index int nOrderIndex = -1; for(int i = 0;i < m_pHeaderCtrl->GetItemCount();++i) { int nCol = m_pHeaderCtrl->OrderToIndex(i); if(nCol == m_FocusCell) { nOrderIndex = i; break; } } // Move to the following column if(bRight)nOrderIndex++; else if(nOrderIndex > 0)nOrderIndex--; // Convert order-index to focus cell if(nOrderIndex >= 0 && nOrderIndex < m_pHeaderCtrl->GetItemCount()) { int nCol = m_pHeaderCtrl->OrderToIndex(nOrderIndex); if(! IsColumnHidden(nCol))m_FocusCell = nCol; } } // Ensure the column is visible if(m_FocusCell >= 0) { VERIFY(EnsureColumnVisible(m_FocusCell, false)); } UpdateFocusCell(m_FocusCell); } BOOL CListCtrlExt::EnsureColumnVisible(int nCol, bool bPartialOK) { if(nCol < 0 || nCol >= m_pHeaderCtrl->GetItemCount())return FALSE; CRect rcHeader; int nScrollX = 0,nOffset = GetScrollPos(SB_HORZ); if(m_pHeaderCtrl->GetItemRect(nCol, rcHeader) == FALSE)return FALSE; CRect rcClient; GetClientRect(&rcClient); if(bPartialOK) { if((rcHeader.left - nOffset < rcClient.right) && (rcHeader.right - nOffset > 0)) { return TRUE; } } if((rcHeader.Width() > rcClient.Width()) || (rcHeader.left - nOffset < 0)) { nScrollX = rcHeader.left - nOffset; } else { if(rcHeader.right - nOffset > rcClient.right) { nScrollX = rcHeader.right - nOffset - rcClient.right; } } if(nScrollX != 0) { CSize size(nScrollX, 0); if(Scroll(size) == FALSE)return FALSE; } return TRUE; } BOOL CListCtrlExt::GetCellRect(int nRow, int nCol, CRect& rect) { // Find the top and bottom of the cell-rectangle CRect rowRect,colRect; if(GetItemRect(nRow, rowRect, LVIR_BOUNDS) == FALSE || m_pHeaderCtrl->GetItemRect(nCol, colRect) == FALSE)return FALSE; // Adjust for scrolling colRect.left -= GetScrollPos(SB_HORZ); colRect.right -= GetScrollPos(SB_HORZ); rect.left = colRect.left; rect.top = rowRect.top; rect.right = colRect.right; rect.bottom = rowRect.bottom; return TRUE; }